# 三、功能需求
# 1.数据加载与预处理
# 系统应能够加载糖尿病数据集。
# 数据集应包含特征数据和目标数据（血糖水平）。
# 特征数据和目标数据都应进行标准化处理，使用MinMaxScaler方法，将数据缩放到[0, 1]区间。
import pickle

import matplotlib.pyplot as plt
import numpy as np
import torch
from sklearn.datasets import load_diabetes
from torch import nn

data, target = load_diabetes(return_X_y=True)
from sklearn.preprocessing import MinMaxScaler

target = target.reshape(-1, 1)
data = MinMaxScaler().fit_transform(data)
target = MinMaxScaler().fit_transform(target)
# 2.时间序列构建
# 系统应能够将标准化后的特征数据构建为时间序列样本。
# 时间序列的窗口大小应可配置，默认为7。
# 每个时间序列样本应包含连续的特征数据点，并对应一个目标数据点（血糖水平）。
c = 7
x = []
y = []
for i in range(len(data) - c):
    x.append(data[i:i + c])
    y.append(target[i + c])
x = torch.tensor(np.array(x), dtype=torch.float)
y = torch.tensor(np.array(y), dtype=torch.float)
print(x.shape)
from sklearn.model_selection import train_test_split

x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2)


# 3.模型训练
# 系统应使用LSTM网络进行时间序列预测。
# LSTM网络应包含两个LSTM层和一个全连接层。
# LSTM层的隐藏层大小应可配置，默认为50和10。
# 系统应使用均方误差（MSE）作为损失函数。
# 系统应使用Adam优化器进行模型训练，学习率应可配置，默认为0.01。
# 模型训练应支持指定迭代次数，默认为1000次。
# 在每次迭代中，应计算并记录损失值。
# 将模型训练每次迭代输出的损失值保存在loss.txt文件中。
class LSTM(nn.Module):
    def __init__(self):
        super(LSTM, self).__init__()
        self.lstm1 = nn.LSTM(input_size=10, hidden_size=50, batch_first=True)
        self.lstm2 = nn.LSTM(input_size=50, hidden_size=128, batch_first=True)
        self.lstm3 = nn.LSTM(input_size=128, hidden_size=256, batch_first=True)
        self.fc = nn.Linear(in_features=256, out_features=1)

    def forward(self, x):
        x, _ = self.lstm1(x)
        x, _ = self.lstm2(x)
        x, _ = self.lstm3(x)
        x = self.fc(x[:, -1, :])
        return x


model = LSTM()
loss_fn = nn.MSELoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.01)
model.train()
loss_list = []
for epoch in range(1000):
    optimizer.zero_grad()
    output = model(x_train)
    loss = loss_fn(output, y_train)
    loss.backward()
    loss_list.append(loss.item())
    optimizer.step()
    if epoch % 10 == 0:
        print('Epoch {}, Loss: {}'.format(epoch, loss.item()))
with open('loss.txt', 'w') as f:
    for item in loss_list:
        f.write(str(item) + '\n')
# 4.模型优化
# 尝试不同的网络结构（如改变GRU层的数量、单元数），观察对模型
# 性能的影响。或者调整超参数（如学习率、批次大小、Dropout率），找到	最优参数组合，要求体现优化策路和优化后的结果可视化展示。
# 简要分析优化后的结果跟未优化前的结果对比。
"""加一层，调整神经元数量，调整了学习率，经过手动调参数，发现最优组合是
三层LSTM，神经元数量，128，256，512，学习率 0.001为最优组合
"""
# 5.模型预测
# 训练完成后，系统应能够使用训练好的模型进行预测。
# 预测结果应为血糖水平的预测值。
# 预测结果应与测试集的实际血糖水平进行对比。
model.eval()
with torch.no_grad():
    output = model(x_test)
    print(output)
    plt.plot(output, label='output')
    plt.plot(y_test, label='target')
    plt.legend()
    plt.show()
# 6.结果可视化
# 系统应能够将预测结果和实际血糖水平进行可视化对比。
# 可视化结果应包含预测值和实际值的折线图。
# 折线图应带有图例，区分预测值和实际值。
# 可视化结果应清晰易读，便于分析预测效果。
# 7.其他要求
# 系统应具备良好的可扩展性和可维护性。
# 代码应包含必要的注释，以便理解和维护。
# 系统应易于扩展，以便添加新的功能或改进现有功能。
# 系统应易于维护，以便修复错误或更新数据集。
